package com.blog.cloud.chat.controller;

import com.blog.cloud.chat.model.ChatMessage;
import com.blog.cloud.chat.service.MessageService;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.stereotype.Controller;

/**
 * 聊天控制器，用于处理聊天消息的接收和发送。
 */
@Controller
@RequiredArgsConstructor
public class ChatController {

    private final SimpMessagingTemplate messagingTemplate;

    private final MessageService messageService;

    // 特别注意： topic 用于群聊消息的接收；queue 用于私聊消息的接收。


    // 关于 @SendTo 和 @SendToUser 的区别 https://springdoc.cn/spring/web.html#websocket-stomp-user-destination
    // 私聊消息的发送，目的地带有 /queue
    @MessageMapping("/chat.private/{receiverId}")      // 类似于HTTP中的@RequestMapping，它用来映射特定的目的地（destination）到控制器的方法上。
    @SendToUser(destinations="/queue/errors", broadcast=false)
    public void privateChat(@DestinationVariable String receiverId, @Payload ChatMessage message) {
        // 发送新消息时保存到数据库
        // TODO 将消息保存到 Mongo 数据库
        messageService.saveMessage(message);

        // 确保消息只发给指定的接收者
        // Spring框架会自动将消息发送到 /user/{username/userId}/queue/private 格式的队列
        // Alice 的私聊消息队列：/user/Alice/queue/private
        messagingTemplate.convertAndSendToUser(receiverId, "/queue/messages", message);
    }

    // 群聊消息的发送, 目的地带有 /topic
    @MessageMapping("/group.{groupId}")
    @SendTo("/topic/group/{groupId}")
    public ChatMessage groupChat(@Payload ChatMessage message, @DestinationVariable String groupId) {
        // 发送新消息时保存到数据库

        // 你可以在这里添加业务逻辑，比如验证用户是否属于该群聊
        return message;
    }

    // 用户第一次被添加到群聊时，发送消息给所有群聊成员
    @MessageMapping("/chat.addUser")
    @SendTo("/topic/messages")
    public ChatMessage addUser(@Payload ChatMessage chatMessage) {
        // 可以在这里添加用户加入逻辑
        return chatMessage;
    }
}

// @Payload 用于访问消息的 payload，由配置的 MessageConverter 转换（例如，从JSON）。
// 这个注解的存在不是必须的，因为默认情况下，如果没有其他参数被匹配，它就会被假定。
// 你可以用 @jakarta.validation.Valid 或Spring的 @Validated 来注解 payload 参数，以使 payload 参数被自动验证。

// @Header 用于访问一个特定的 header 值

// @DestinationVariable 用于访问从消息 destination 提取的模板变量。必要时，值被转换为声明的方法参数类型。
// Principal 反映在WebSocket HTTP握手时登录的用户。


// 默认情况下，@MessageMapping 方法的返回值通过匹配的 MessageConverter 被序列化为一个 payload，并作为一个 Message 发送到 brokerChannel，
// 从那里被广播给订阅者。出站消息的 destination 与入站消息的 destination 相同，但前缀为 /topic。

// 你可以使用 @SendTo 和 @SendToUser 注解来定制输出消息的 destination。@SendTo 是用来定制目标 destination 或指定多个 destination 的。
// @SendToUser 用来指导输出消息只给与输入消息相关的用户。
// @SendTo 和 @SendToUser 只是一种便利，相当于使用 SimpMessagingTemplate 来发送消息。如果有必要，对于更高级的场景， @MessageMapping 方法可以直接使用 SimpMessagingTemplate。这可以代替返回值，也可以在返回值之外进行。

// 消息可以被异步处理，@MessageMapping 方法可以返回 ListenableFuture、 CompletableFuture 或 CompletionStage。

// 一个应用程序可以使用 @MessageExceptionHandler 方法来处理来自 @MessageMapping 方法的异常。